/*----------------------------------------------------------------------------*\
**
**								Heroes IV
**					  Copyright 2002, The 3DO Company
**
**	------------------------------------------------------------------
** 						       dialog_combat_creature.h
**
**	$Header: /game/dialog_combat_creature.h $
**
**	$NoKeywords: $
**
\-----------------------------------------------------------------------------*/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "dialog_combat_creature.h"

#include "battlefield.h"
#include "bitmap_layer_cache_window.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "combat_creature.h"
#include "creature.h"
#include "creature_traits.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "hero.h"
#include "interface_help.h"
#include "luck_icon_window.h"
#include "morale_icon_window.h"
#include "replace_keywords.h"
#include "scrollbar.h"
#include "skill_icon.h"
#include "spell.h"
#include "spell_icon_window.h"
#include "spell_properties.h"
#include "standard_fonts.h"
#include "text_window.h"
#include "town_properties.h"

static  t_bitmap_group_cache const k_bitmaps( "dialog.combat.creature" );

extern  t_external_string const	k_text_class_level;
extern  t_external_string const k_text_infinity_abbreviation;

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
static t_external_string const k_text_melee( "melee.dialog" );
static t_external_string const k_text_ranged( "Ranged.dialog" );
extern t_external_string const k_text_hit_points;
static t_external_string const k_text_wounds( "Wounds.dialog" );
static t_external_string const k_text_melee_defense( "melee_defense.dialog" );
static t_external_string const k_text_ranged_defense( "Ranged_defense.dialog" );
extern t_external_string const k_text_movement;
extern t_external_string const k_text_speed;
extern t_external_string const k_text_shots;
extern t_external_string const k_text_spell_points;
extern t_external_string const k_text_not_applicable;
extern t_button_cache g_ok_button;

t_dialog_combat_creature::t_dialog_combat_creature( t_combat_creature const * creature, 
												    t_battlefield_ptr battlefield,
												    t_screen_point const& window_origin,
													t_window* parent, 
													t_screen_rect const& parent_rect )
					    : t_popup_window( k_completely_transparent, parent ),
						  m_battlefield( battlefield )
{
	t_screen_rect rect;

	// get bitmaps
	m_bitmaps = k_bitmaps.get();
	
    // position window to be centered and above point
    //t_screen_rect parent_rect = get_parent()->get_client_rect();
	rect = m_bitmaps->get_rect();
    int left = (parent_rect.right - rect.right) / 3;
    int top = (parent_rect.bottom - rect.bottom) / 2;
	rect += t_screen_point( left, top );
	init( rect );

	// create background 
	t_bitmap_layer const* layer;
	t_window*			  window;

	m_background = create_bitmap( "background" );
	m_origin = m_bitmaps->find( "background" )->get_rect().top_left();
	create_bitmap( "top" );
	create_bitmap( "left" );
	create_bitmap( "right" );
	create_bitmap( "bottom" );

	// display portrait
	t_bitmap_layer_ptr	icon;
	
	rect = m_bitmaps->find( "portrait" )->get_rect() - m_origin;
	icon = creature->get_portrait( 82 ); //rect.height() );
	window = new t_bitmap_layer_cache_window( icon, rect.top_left(), m_background );
	layer = m_bitmaps->find( "portrait_ring" );
	window = new t_bitmap_layer_window( layer, -m_origin, m_background );

    create_stats_icons();
    create_stats_text( creature );
	create_spells( creature );

	// create Close button
	t_help_block const&     shared_help = get_help_block( "shared" );

	rect = m_bitmaps->find( "ok_button" )->get_rect() - m_origin;

	t_button* ok_button = new t_button( g_ok_button.get(), rect.top_left(), this );
	t_button_handler handler;

	handler = bound_handler( *this, &t_dialog_combat_creature::close_click );
	ok_button->set_click_handler( handler );
	set_help( ok_button, shared_help, "ok" );
}

void t_dialog_combat_creature::create_stats_icons()
{
	t_bitmap_layer const*   layer;
	t_bitmap_layer const*   value_layer;
	t_window*               window;
    t_screen_rect           rect;
	t_screen_point          origin(0,0);
	t_screen_point          offset(0,0);
	t_screen_point          point(0,0);
	t_help_block const&     shared_help = get_help_block( "shared" );
	t_help_block const&     help = get_help_block( "army_screen" );
	t_help_block const&     combat_creature_help = get_help_block( "combat_creature" );

    // Get text background offset.
	value_layer = m_bitmaps->find( "value_background" );
	layer = m_bitmaps->find( "shots"); //damage" );
	offset = -layer->get_rect().top_left();

	layer = m_bitmaps->find( "damage" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "damage" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "melee_attack" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, help, "melee_attack" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "melee_defense" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, help, "melee_defense" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "hit_points" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "hit_points" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "hits_left" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "hits_left" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

    // Morale
	rect = m_bitmaps->find( "morale" )->get_rect();
	point = rect.top_left();
	m_morale_icon = new t_morale_icon_window( rect, this );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

    // Luck
	rect = m_bitmaps->find( "luck" )->get_rect();
	point = rect.top_left();
	m_luck_icon = new t_luck_icon_window( rect, this );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "ranged_attack" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, help, "ranged_attack" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "ranged_defense" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, help, "ranged_defense" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "shots" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "shots" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "spell_points" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "spell_points" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "movement" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "movement" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "speed" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, shared_help, "speed" );
	window = new t_bitmap_layer_window( value_layer, point + offset, this );

	layer = m_bitmaps->find( "curse_icon" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, combat_creature_help, "cursed_spells" );

	layer = m_bitmaps->find( "bless_icon" );
	point = layer->get_rect().top_left();
	window = new t_bitmap_layer_window( layer, origin, this );
	set_help( window, combat_creature_help, "blessed_spells" );

    // Skill icons.
    for (int i=0; i<k_max_skills_to_display; i++)
    {
        rect = t_screen_rect(0, 0, 52, 52);
	    point = m_bitmaps->find(format_string( "skill_%i", i+1))->get_rect().top_left();
        rect += point;
        rect += origin;
		m_skill_icons[i] = new t_skill_icon( rect, this );
    }
	
    layer = m_bitmaps->find( "abilities_frame" );
	window = new t_bitmap_layer_window( layer, origin, this );
}

void t_dialog_combat_creature::create_stats_text( t_combat_creature const * creature )
{
	std::string		text;
	t_creature_stack const * stack = creature->get_creature_stack();

    // Create text windows.
	t_pixel_24      color(0,0,0);
	t_pixel_24      drop_shadow_color(200,200,200);
	t_hero const*   hero;

	text = capitalize_words(creature->get_name());
	hero = creature->get_hero();

	if ( hero )
	{
		// Name
		create_text_window ( "name", text );

		// Level.
		text = replace_keywords( k_text_class_level, "%level", 
			                      format_string( "%i", hero->get_level() ));
		create_text_window ( "level", text );

		// Class.
		text = hero->get_class_name();
		std::string help_text = hero->get_class_description();
		create_text_window ( "class", text, 0, k_justify_center, help_text );
	}
	else
	{
		// Number and name
		text = format_string( "%i ", creature->get_number() ) + text;
		create_text_window ( "name", text );

		// Alignment
		text = get_alignment_name( creature->get_alignment() );
		create_text_window ( "alignment", text );
	}


    // Hit points.
	int base_hit_points = stack->get_hit_points();
	int hit_points = creature->get_hit_points();
	int hit_points_bonus;
	
    if (creature->is_active(k_spell_celestial_armor))
		hit_points += creature->get_effect_power( k_spell_celestial_armor );
	if (creature->is_active(k_spell_life_drain))
		hit_points += creature->get_effect_power( k_spell_life_drain );
	hit_points_bonus = (hit_points - base_hit_points) * 100 / base_hit_points;
	text = format_string( "%i", hit_points );
	if (hit_points_bonus != 0)
		text += format_string( " (%+i%%)", hit_points_bonus );
	create_text_window( "hit_points_value", text );
	text = format_string( "%i", creature->get_remaining_hitpoints() );
	create_text_window( "wounds_value", text );

    // Melee offense/defense	
	float		melee_offense = creature->get_offense( false );
	float       ranged_offense = creature->get_offense( true );
	int			damage_low  = creature->get_damage_low();
	int			damage_high = creature->get_damage_high();
	int			fire_bonus = 0;
	if (creature->is_active( k_spell_firebreathing ))
	{
		fire_bonus = creature->get_effect_power( k_spell_firebreathing );
	}
	//text = format_string( "%i-%i", damage_low, damage_high );
	//if (melee_offense != 1.0f)
	text = format_string( "%i", int( melee_offense * 10.0f + 0.5f ) );

	if (fire_bonus != 0)
		text += format_string( " (%+i)", fire_bonus );
	create_text_window( "melee_attack_value", text );

	if ( damage_low < 1 )
		damage_low = 1;

	if ( damage_high < 1 )
		damage_high = 1;

	text = format_string( "%i-%i", damage_low, damage_high );
	create_text_window( "damage_value", text, 16 );

    // Ranged ability?  Else show 'n/a'
	if (creature->has_ability( k_ability_ranged ))
	{
		std::string shots_text;

	    if (creature->has_ability( k_ability_unlimited_shots ))
        {
		    shots_text = k_text_infinity_abbreviation;
        }
        else //if (!creature->has_ability( k_ability_unlimited_shots ))
		{
			shots_text = format_string( " %i ", creature->get_shots() );
			shots_text += k_text_shots;
		}
		text = format_string( "%i", int( ranged_offense * 10.0f + 0.5f ) );
		create_text_window( "ranged_attack_value", text );
		create_text_window( "shots_value", shots_text );
	}
    else
    {
		create_text_window( "ranged_attack_value", k_text_not_applicable, 16 );
		create_text_window( "shots_value", k_text_not_applicable, 16 );
    }

    float melee_defense = creature->get_defense_basic( false ) 
		                * creature->get_defense_bonus( false );
	float ranged_defense = creature->get_defense_basic( true )
					 	 * creature->get_defense_bonus( true );

	text = format_string( "%i", int( melee_defense * 10.0f + 0.5f ) );
	create_text_window( "melee_defense_value", text );
	
    text = format_string( "%i", int( ranged_defense * 10.0f + 0.5f ));
	create_text_window( "ranged_defense_value", text );

    // Movement.
	int movement = creature->get_combat_movement() / 300;
	int movement_bonus = movement - stack->get_combat_movement() / 300;
	text = format_string( "%i", movement );
	if (movement_bonus != 0)
		text += format_string( " (%+i)", movement_bonus);
	create_text_window( "movement_value", text );

    // Speed.
	int speed_bonus;
	int speed;
	speed = creature->get_speed();
	speed_bonus = speed - creature->get_creature_stack()->get_speed();
	if (speed_bonus != 0)
		text = format_string( "%i (%+i)", speed, speed_bonus );
	else
		text = format_string( "%i", speed );
	create_text_window( "speed_value", text );

	// Spell points
	int spell_points;

	spell_points = creature->get_spell_points();
	text = format_string( "%i", spell_points );
	create_text_window( "spell_points_value", text );

    // Morale.
	int morale = creature->get_morale();
	if (morale == 0)
		text = "0";
	else 
		text = format_string( "%+i", morale );
	create_text_window( "morale_text", text );

	int luck = creature->get_luck();
	if (luck == 0)
		text = "0";
	else
		text = format_string( "%+i", luck );
	create_text_window( "luck_text", text );

    // Skill icons.
    int i;
    if (hero)
    {
	    std::vector<t_skill> skills = hero->get_skills();

        int num_skills_to_display = skills.size();

        if (num_skills_to_display > k_max_skills_to_display)
            num_skills_to_display = k_max_skills_to_display;

        for (i=0; i< num_skills_to_display; i++)
        {
            m_skill_icons[i]->set_skill ( skills[i] );
        }
    }
    // Else show creature traits.
    else
    {
	    t_creature const * creature_ptr = stack->get_creature();
	    t_creature_traits const& creature_traits = creature_ptr->get_traits();
	    std::vector<t_creature_ability> abilities = creature_traits.abilities;

        for (i=0; i< abilities.size(); i++)
        {
            m_skill_icons[i]->set_ability ( abilities[i] );
        }
        
    }
}


// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
bool t_dialog_combat_creature::children_handle_mouse()
{
	return true;
}

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
void t_dialog_combat_creature::close_click( t_button* button )
{
	close();
}

// ---------------------------------------------------------------
// Called when dialog is closed for any reason
// ---------------------------------------------------------------
void t_dialog_combat_creature::on_close()
{
	// Inform battlefield
	m_battlefield->right_click_dialog_closed();

	t_popup_window::on_close();
}

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
void t_dialog_combat_creature::show_cursed_spells( t_scrollbar*, int top_spell )
{
	int i;

	top_spell *= k_spell_icon_count;
	for (i = 0; i < k_spell_icon_count && i + top_spell < m_cursed_spells.size(); i++)
	{
		m_cursed_spell_windows[i]->set_spell( m_cursed_spells[i + top_spell] );
	}
	while (i < k_spell_icon_count)
	{
		m_cursed_spell_windows[i]->set_spell( k_spell_none );
		i++;
	}
}

void t_dialog_combat_creature::show_blessed_spells( t_scrollbar*, int top_spell )
{
	int i;

	top_spell *= k_spell_icon_count;
	for (i = 0; i < k_spell_icon_count && i + top_spell < m_blessed_spells.size(); i++)
	{
		m_blessed_spell_windows[i]->set_spell( m_blessed_spells[i + top_spell] );
	}
	while (i < k_spell_icon_count)
	{
		m_blessed_spell_windows[i]->set_spell( k_spell_none );
		i++;
	}
}

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
void t_dialog_combat_creature::create_spells( t_combat_creature const * creature )
{
	t_spell       spell;
	int           i;
	t_screen_rect rect;
	std::string   layer_name;

    // Curse.
	m_cursed_spells.clear();
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if ((creature->is_active( spell )) && is_curse( spell ))
			m_cursed_spells.push_back( spell );
	}
	for (i = 0; i < m_cursed_spells.size() && i < k_spell_icon_count; i++)
	{
		layer_name = format_string( "curse_spell_%i", (i + 1) );
		rect = m_bitmaps->find( layer_name )->get_rect() - m_origin;
		m_cursed_spell_windows[i] = new t_spell_icon_window( m_cursed_spells[i], rect, m_background );
	}
	while (i < k_spell_icon_count)
	{
		m_cursed_spell_windows[i] = 0;
		i++;
	}

	if (m_cursed_spells.size() > k_spell_icon_count)
	{
		t_scrollbar*        scrollbar;
		t_scrollbar_handler handler;

		rect = m_bitmaps->find( "curse_scrollbar" )->get_rect() - m_origin;
		scrollbar = new t_scrollbar( rect.top_left(), rect.height(), m_background, 0,
			                         (m_cursed_spells.size() - 1 ) / k_spell_icon_count  );
		handler = bound_handler( *this, &t_dialog_combat_creature::show_cursed_spells );
		scrollbar->set_handler( handler );
	}

    // Blessing.
    m_blessed_spells.clear();
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if ((creature->is_active( spell )) && is_blessing( spell ))
			m_blessed_spells.push_back( spell );
	}
	for (i = 0; i < m_blessed_spells.size() && i < k_spell_icon_count; i++)
	{
		layer_name = format_string( "blessing_spell_%i", (i + 1) );
		rect = m_bitmaps->find( layer_name )->get_rect() - m_origin;
		m_blessed_spell_windows[i] = new t_spell_icon_window( m_blessed_spells[i], rect, m_background );
	}
	while (i < k_spell_icon_count)
	{
		m_blessed_spell_windows[i] = 0;
		i++;
	}

	if (m_blessed_spells.size() > k_spell_icon_count)
	{
		t_scrollbar*        scrollbar;
		t_scrollbar_handler handler;

		rect = m_bitmaps->find( "blessing_scrollbar" )->get_rect() - m_origin;
		scrollbar = new t_scrollbar( rect.top_left(), rect.height(), m_background, 0,
			                         (m_blessed_spells.size() - 1 ) / k_spell_icon_count  );
		handler = bound_handler( *this, &t_dialog_combat_creature::show_blessed_spells );
		scrollbar->set_handler( handler );
	}
}

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
void t_dialog_combat_creature::create_text( char const* layer_name, std::string const& text,
										    t_justification justification )
{
	t_screen_rect  rect;
	t_text_window* text_window;

	rect = m_bitmaps->find( layer_name )->get_rect(); // - m_origin;
	text_window = new t_text_window( get_font( rect.height() ), rect, this, //m_background,
			                         text, t_pixel_24(0,0,0));
	text_window->set_drop_shadow( false );
	text_window->set_justification( justification );
}

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
void t_dialog_combat_creature::create_text_window( char const* layer_name, std::string const& text, int font_size,
										           t_justification justification,
												   std::string const& help_text )
{
	t_screen_rect  rect;
	t_text_window* text_window;
    int             font;

	rect = m_bitmaps->find( layer_name )->get_rect();

    if (font_size)
        font = font_size;
    else
        font = rect.height();

	text_window = new t_text_window( get_font( font ), rect, this,
			                         text, t_pixel_24(0,0,0));
	text_window->set_drop_shadow( false );
	text_window->set_justification( justification );
	text_window->set_center_vertical();
	text_window->set_right_click_text( help_text );
}

// ---------------------------------------------------------------
// popup dialog to display info about a combat creature
// ---------------------------------------------------------------
t_window* t_dialog_combat_creature::create_bitmap( char const* name )
{
	t_bitmap_layer const* layer;

	layer = m_bitmaps->find( name );
	return new t_bitmap_layer_window( layer, t_screen_point(0,0), this );
}
